package com.heima.article.service.impl;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.heima.article.entity.ApArticle;
import com.heima.article.service.IApArticleService;
import com.heima.article.service.IHotArticleService;
import com.heima.behavior.dto.ArticleStreamMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author: itheima
 * @create: 2022-04-03 11:51
 */
@Service
public class HotArticleServiceImpl implements IHotArticleService {

    @Autowired
    private IApArticleService apArticleService;

    @Autowired
    private StringRedisTemplate redisTemplate;


    /**
     * 查询最近五天发布文章，根据文章操作类型计算分值；将数据缓存到Redis中Zset数据结构中
     */
    @Override
    public void compute() {
        //1.查询文章列表（上架未删除）的文章列表
        LambdaQueryWrapper<ApArticle> queryWrapper = new LambdaQueryWrapper<>();
        //1.1 设置上架状态
        queryWrapper.eq(ApArticle::getIsDown, false);
        //1.2 设置未删除状态
        queryWrapper.eq(ApArticle::getIsDelete, false);

        //1.3 设置日期查询区间
        Date from = getFromAndToDate(-30);
        Date to = getFromAndToDate(0);
        queryWrapper.between(ApArticle::getPublishTime, from, to);
        List<ApArticle> list = apArticleService.list(queryWrapper);

        //2.遍历文章列表，计算分值。将文章缓存到redis中
        //2.1 定义排行榜key  推荐 其他频道的zset排行榜key
        String redisKeyPrefix = "leadnews:article:hot:page:";

        //2.2 计算每篇文章得分
        for (ApArticle article : list) {
            int score = computeScore(article);

            //2.3 将文章数据（json）存入redis中 zset数据结构中，设置过期时间为1天
            ApArticle cache = new ApArticle();
            cache.setId(article.getId());
            cache.setTitle(article.getTitle());
            cache.setImages(article.getImages());
            cache.setAuthorId(article.getAuthorId());
            cache.setAuthorName(article.getAuthorName());
            cache.setLayout(article.getLayout());
            cache.setStaticUrl(article.getStaticUrl());

            String value = JSON.toJSONString(cache);

            //2.3.1 首页数据
            String indexKey = redisKeyPrefix + "0";
            redisTemplate.opsForZSet().add(indexKey, value, score);
            redisTemplate.expire(indexKey, 1, TimeUnit.DAYS);

            //2.3.2 其他频道
            String channelKey = redisKeyPrefix + article.getChannelId();
            redisTemplate.opsForZSet().add(channelKey, value, score);
            redisTemplate.expire(channelKey, 1, TimeUnit.DAYS);
        }
    }


    /**
     * 获取指定天数日期 正数：未来时间  负数：以前时间
     *
     * @param datecount
     * @return
     */
    public static Date getFromAndToDate(Integer datecount) {
        //创建日期对象
        Calendar calendar = Calendar.getInstance();
        //设置日期
        calendar.add(Calendar.DATE, datecount);
        //设置时分秒
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        return calendar.getTime();
    }


    /**
     * 为入参的文章计算分值
     *
     * @param article
     * @return
     */
    private int computeScore(ApArticle article) {
        int score = 0;
        // 阅读 +1  点赞 +3  评论 +5  收藏 +8
        if (article.getViews() != null) {
            score += article.getViews() * 1;
        }
        if (article.getLikes() != null) {
            score += article.getLikes() * 3;
        }
        if (article.getComment() != null) {
            score += article.getComment() * 5;
        }
        if (article.getCollection() != null) {
            score += article.getCollection() * 8;
        }
        return score;
    }


    /**
     * 通过得到聚合文章操作结果，更新redis缓存，以及数据库中文章记录各种操作数值
     *
     * @param articleStreamMessage
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateCacheAndInfo(ArticleStreamMessage articleStreamMessage) {
        //1.根据文章ID查询文章对象
        ApArticle article = apArticleService.getById(articleStreamMessage.getArticleId());
        //2.构建Redis缓存文章对象。一定要跟以前放入缓存属性一致
        ApArticle cache = new ApArticle();
        cache.setId(article.getId());
        cache.setTitle(article.getTitle());
        cache.setImages(article.getImages());
        cache.setAuthorId(article.getAuthorId());
        cache.setAuthorName(article.getAuthorName());
        cache.setLayout(article.getLayout());
        cache.setStaticUrl(article.getStaticUrl());
        String value = JSON.toJSONString(cache);

        //3.更新redis缓存  两种情况:缓存中有 缓存中没有
        //3.1 更新redis首页中缓存，以及其他频道
        String redisKeyPrefix = "leadnews:article:hot:page:";
        String indexKey = redisKeyPrefix + "0";
        String channelKey = redisKeyPrefix + article.getChannelId();
        //3.2 计算聚合后计算增量得分
        int socrePlus = computeScore(articleStreamMessage);
        //3.3. 更新不同缓存数据
        Double score = redisTemplate.opsForZSet().score(indexKey, value);
        Double channelScore = redisTemplate.opsForZSet().score(channelKey, value);
        if (score != null) {
            //3.3.1 缓存中已有数据，直接在原来redis中文章得分上+增量得分
            redisTemplate.opsForZSet().incrementScore(indexKey, value, socrePlus);
            redisTemplate.opsForZSet().incrementScore(channelKey, value, socrePlus);
        } else {
            //3.3.2 缓存中不存在该文章 计算现有历史得分+增量得分
            int historyScore = computeScore(article);
            redisTemplate.opsForZSet().add(indexKey, value, historyScore + socrePlus);
            redisTemplate.opsForZSet().add(channelKey, value, historyScore + socrePlus);
        }
        //4.更新数据库中文章记录的各种数值：阅读，点赞，评论，收藏等数值
        LambdaUpdateWrapper<ApArticle> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(ApArticle::getId, article.getId());
        updateWrapper.setSql("views = views +" + articleStreamMessage.getView());
        updateWrapper.setSql("likes = likes +" + articleStreamMessage.getLike());
        updateWrapper.setSql("comment = comment +" + articleStreamMessage.getComment());
        updateWrapper.setSql("collection = collection +" + articleStreamMessage.getCollect());
        apArticleService.update(updateWrapper);

    }

    /**
     * 计算当日的增量分数  当日的操作整体权重*3
     *
     * @param message
     * @return
     */
    private int computeScore(ArticleStreamMessage message) {
        int score = 0;
        score += message.getView() * 1 * 3;
        score += message.getLike() * 3 * 3;
        score += message.getComment() * 5 * 3;
        score += message.getCollect() * 8 * 3;
        return score;
    }

}
